uniform extern float4x4 gWVP;
uniform extern float4x4 gWorld;
uniform extern float4x4 gWorldInvT;
uniform extern float3 gEyePosW;

uniform extern float4 gDiffMat;
uniform extern float4 gAmbMat;
uniform extern float4 gSpecMat;
uniform extern float gSpecPower;

uniform extern float4 gDiffLight;
uniform extern float4 gAmbLight;
uniform extern float4 gSpecLight;
uniform extern float3 gMainLightDirW;

uniform extern float4 gPL1Diff;
uniform extern float3 gPL1Att012;
uniform extern float3 gPL1Pos;

uniform extern float4 gPL2Diff;
uniform extern float3 gPL2Att012;
uniform extern float3 gPL2Pos;

uniform extern float4 gPL3Diff;
uniform extern float3 gPL3Att012;
uniform extern float3 gPL3Pos;

uniform extern texture gTex;
uniform extern texture gTexNormal;
uniform extern texture gTexBump;

sampler TexS = sampler_state
{
  Texture = <gTex>;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
  MipFilter = LINEAR;
};

sampler TexNormalS = sampler_state
{
  Texture = <gTexNormal>;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
  MipFilter = LINEAR;
};


sampler TexBumpS = sampler_state
{
  Texture = <gTexBump>;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
  MipFilter = LINEAR;
};



struct OutputVS
{
  float4 posH  : POSITION0;
  float3 normalW : TEXCOORD0;
  float3 posW : TEXCOORD1;
  float2 texC : TEXCOORD2;
  float3 addLight : TEXCOORD3;
};


struct OutputTBN_VS
{
  float4 posH  : POSITION0;
  float3 normalW : TEXCOORD0;
  float3 tangentW : TEXCOORD4;
  float3 binormalW : TEXCOORD5;
  float3 posW : TEXCOORD1;
  float2 texC : TEXCOORD2;
  float3 addLight : TEXCOORD3;
};


// computes pervertex lightning for three lights passed in uniforms
// I'm wondering how it fits into vs_1_1 o_O, I thought I'll need at least 3_0 to pack all this stuff into single pass.
OutputVS PhongVS(float3 posL : POSITION0, float3 normalL : NORMAL0, float2 texC : TEXCOORD0)
{
  OutputVS outVS = (OutputVS)0;
	
  outVS.posH = mul(float4(posL, 1.0f), gWVP);
  float3 posW = mul(float4(posL, 1.0f), gWorld).xyz;
  outVS.posW = posW;
	
  float3 normalW = mul(float4(normalL, 0.0f), gWorldInvT).xyz;
  normalW = normalize(normalW);
  outVS.normalW = normalW;

  outVS.texC = texC;


  //float3 vertToEyeW = normalize(gEyePosW - posW);
  
  outVS.addLight = 0;
  
  float3 lightVecW = normalize(gPL1Pos - posW);
  
  float diffLightFactor = max(dot(normalW, lightVecW), 0);
  float3 diffuse = diffLightFactor * (gDiffMat * gPL1Diff).rgb;
  
  float d = distance(gPL1Pos, posW) * 0.0001f; // scale to convenience
  float att = gPL1Att012.x + gPL1Att012.y*d + gPL1Att012.z*d*d;
  
  outVS.addLight += diffuse / att;
  
  
  lightVecW = normalize(gPL2Pos - posW);
  
  diffLightFactor = max(dot(normalW, lightVecW), 0);
  diffuse = diffLightFactor * (gDiffMat * gPL2Diff).rgb;
  
  d = distance(gPL2Pos, posW) * 0.0001f; // scale to convenience
  att = gPL2Att012.x + gPL2Att012.y*d + gPL2Att012.z*d*d;
  
  outVS.addLight += diffuse / att;
  
  
  lightVecW = normalize(gPL3Pos - posW);
  
  diffLightFactor = max(dot(normalW, lightVecW), 0);
  diffuse = diffLightFactor * (gDiffMat * gPL3Diff).rgb;
  
  d = distance(gPL3Pos, posW) * 0.0001f; // scale to convenience
  att = gPL3Att012.x + gPL3Att012.y*d + gPL3Att012.z*d*d;
  
  outVS.addLight += diffuse / att;
  
  
  return outVS;
}



// version that transfers tangents and binormals to pixes shader
OutputTBN_VS PhongTBN_VS(float3 posL : POSITION0, float3 normalL : NORMAL0, float3 tangentL : TANGENT0, float3 binormalL : BINORMAL0,
								 float2 texC : TEXCOORD0)
{
  OutputTBN_VS outVS = (OutputTBN_VS)0;
	
  outVS.posH = mul(float4(posL, 1.0f), gWVP);
  float3 posW = mul(float4(posL, 1.0f), gWorld).xyz;
  outVS.posW = posW;
	
  float3 normalW = mul(float4(normalL, 0.0f), gWorldInvT).xyz;
  normalW = normalize(normalW);
  outVS.normalW = normalW;
  
  float3 T = mul(float4(tangentL, 0.0f), gWorldInvT).xyz;
  T = normalize(T);
  outVS.tangentW = T;
  
  float3 B = mul(float4(binormalL, 0.0f), gWorldInvT).xyz;
  B = normalize(B);
  outVS.binormalW = B;
  
  outVS.texC = texC;


  //float3 vertToEyeW = normalize(gEyePosW - posW);
  
  outVS.addLight = 0;
  
  float3 lightVecW = normalize(gPL1Pos - posW);
  
  float diffLightFactor = max(dot(normalW, lightVecW), 0);
  float3 diffuse = diffLightFactor * (gDiffMat * gPL1Diff).rgb;
  
  float d = distance(gPL1Pos, posW) * 0.0001f; // scale to convenience
  float att = gPL1Att012.x + gPL1Att012.y*d + gPL1Att012.z*d*d;
  
  outVS.addLight += diffuse / att;
  
  
  lightVecW = normalize(gPL2Pos - posW);
  
  diffLightFactor = max(dot(normalW, lightVecW), 0);
  diffuse = diffLightFactor * (gDiffMat * gPL2Diff).rgb;
  
  d = distance(gPL2Pos, posW) * 0.0001f; // scale to convenience
  att = gPL2Att012.x + gPL2Att012.y*d + gPL2Att012.z*d*d;
  
  outVS.addLight += diffuse / att;
  
  
  lightVecW = normalize(gPL3Pos - posW);
  
  diffLightFactor = max(dot(normalW, lightVecW), 0);
  diffuse = diffLightFactor * (gDiffMat * gPL3Diff).rgb;
  
  d = distance(gPL3Pos, posW) * 0.0001f; // scale to convenience
  att = gPL3Att012.x + gPL3Att012.y*d + gPL3Att012.z*d*d;
  
  outVS.addLight += diffuse / att;
  
  
  return outVS;
}




// version without normal mapping
float4 PhongPS(float3 normalW : TEXCOORD0,
				float3 posW : TEXCOORD1,
				float2 texC : TEXCOORD2,
				float3 addLight : TEXCOORD3) : COLOR
{
  normalW = normalize(normalW);

  float3 lightVecW = normalize(gMainLightDirW);
  float3 vertToEyeW = normalize(gEyePosW - posW);
  
  float diffLightFactor = max(dot(normalW, lightVecW), 0);
  float3 diffuse = diffLightFactor * (gDiffMat * gDiffLight).rgb;
  
  float3 ambient = (gAmbMat * gAmbLight).rgb;
  
  float3 refl = reflect(normalize(-lightVecW), normalW);
  float specLightFactor = pow(max(dot(refl, vertToEyeW), 0.0f), gSpecPower);
  float3 specular = specLightFactor * (gSpecMat * gSpecLight).rgb;
  
  float3 texColor = tex2D(TexS, texC).rgb;
  
  return float4(ambient * texColor + ((diffuse + addLight) * texColor + specular), gDiffMat.a);
}


// used by PhongNormalGenTanPS
float3x3 MatrixInverse(float3x3 inMatrix)
{  
   float det = dot(cross(inMatrix[0], inMatrix[1]), inMatrix[2]);
   float3x3 T = transpose(inMatrix);
   return float3x3(cross(T[1], T[2]),
               cross(T[2], T[0]),
               cross(T[0], T[1])) / det;
}


// this version of pixel shader generates tangent frames each time it executes
float4 PhongNormalGenTanPS(float3 normalW : TEXCOORD0,
				float3 posW : TEXCOORD1,
				float2 texC : TEXCOORD2,
				float3 addLight : TEXCOORD3) : COLOR
{
  float3 dpx = ddx(posW);
  float3 dpy = ddy(posW);
  float2 dtx = ddx(texC);
  float2 dty = ddy(texC);
  
  float3x3 M = float3x3(dpx, dpy, cross(dpx, dpy));
  float3x3 inverseM = MatrixInverse(M);
  float3 T = mul(inverseM, float3(dtx.x, dty.x, 0.0));
  float3 B = mul(inverseM, float3(dtx.y, dty.y, 0.0));

  float maxLength = max(length(T), length(B));
  T = T / maxLength;
  B = B / maxLength;
   
  T = normalize(T);
  B = normalize(B);  

  
  //float3 T = normalize(dpx*dty.y - dpy*dtx.y);
  //float3 B = normalize(-dpx*dty.x + dpy*dtx.x);

  float3x3 TBN = float3x3(T, B, normalize(normalW));
  float3x3 WorldToTangent = TBN; // tangentToWorld actually i think

  

  float3 normalT = 2 * tex2D(TexNormalS, texC) - 1;


  normalW = normalize(mul(normalize(normalT),WorldToTangent));
  
  float diffLightFactor = max(dot(normalW, gMainLightDirW), 0);
  float3 diffuse = diffLightFactor * (gDiffMat * gDiffLight).rgb;
  
  float3 ambient = (gAmbMat * gAmbLight).rgb;
  
  float3 vertToEyeW = normalize(gEyePosW - posW);
  float3 refl = reflect(normalize(-gMainLightDirW), normalW);
  float specLightFactor = pow(max(dot(refl, vertToEyeW), 0.0f), gSpecPower);
  float3 specular = specLightFactor * (gSpecMat * gSpecLight).rgb;
  
  float3 texColor = tex2D(TexS, texC).rgb;
  
  return float4(ambient * texColor + ((diffuse + addLight) * texColor + specular), gDiffMat.a);
}



// this one uses tangents and binormals from vertex data
float4 PhongNormalPS(float3 normalW : TEXCOORD0,
float3 tangentW : TEXCOORD4,
  float3 binormalW : TEXCOORD5,
				float3 posW : TEXCOORD1,
				float2 texC : TEXCOORD2,
				float3 addLight : TEXCOORD3) : COLOR
{  
  float3 T = normalize(tangentW);
  float3 B = normalize(binormalW);  


  float3x3 TBN = float3x3(T, B, normalize(normalW));

  float3 normalT = 2 * tex2D(TexNormalS, texC) - 1;

  normalW = normalize(mul(normalize(normalT),TBN));
  
  float diffLightFactor = max(dot(normalW, gMainLightDirW), 0);
  float3 diffuse = diffLightFactor * (gDiffMat * gDiffLight).rgb;
  
  float3 ambient = (gAmbMat * gAmbLight).rgb;
  
  float3 vertToEyeW = normalize(gEyePosW - posW);
  float3 refl = reflect(normalize(-gMainLightDirW), normalW);
  float specLightFactor = pow(max(dot(refl, vertToEyeW), 0.0f), gSpecPower);
  float3 specular = specLightFactor * (gSpecMat * gSpecLight).rgb;
  
  float3 texColor = tex2D(TexS, texC).rgb;
  
  return float4(ambient * texColor + ((diffuse + addLight) * texColor + specular), gDiffMat.a);
}



// and this one has also parallax mapping, besides normal mapping
float4 PhongParallaxNormalPS(float3 normalW : TEXCOORD0,
float3 tangentW : TEXCOORD4,
  float3 binormalW : TEXCOORD5,
				float3 posW : TEXCOORD1,
				float2 texC : TEXCOORD2,
				float3 addLight : TEXCOORD3) : COLOR
{  
  float3 T = normalize(tangentW);
  float3 B = normalize(binormalW);  

  float3x3 TBN = float3x3(T, B, normalize(normalW));
  
  float3 vertToEyeW = normalize(gEyePosW - posW);
  
  float height = (tex2D(TexBumpS, texC).r - 0.5f) * 0.1f;
  float3 vertToEyeTBN = normalize(mul(TBN, vertToEyeW));
  texC += vertToEyeTBN.xy * height;
  
  

  float3 normalT = 2 * tex2D(TexNormalS, texC) - 1;

  normalW = normalize(mul(normalize(normalT),TBN));
  
  float diffLightFactor = max(dot(normalW, gMainLightDirW), 0);
  float3 diffuse = diffLightFactor * (gDiffMat * gDiffLight).rgb;
  
  float3 ambient = (gAmbMat * gAmbLight).rgb;
  
  
  float3 refl = reflect(normalize(-gMainLightDirW), normalW);
  float specLightFactor = pow(max(dot(refl, vertToEyeW), 0.0f), gSpecPower);
  float3 specular = specLightFactor * (gSpecMat * gSpecLight).rgb;
  
  float3 texColor = tex2D(TexS, texC).rgb;
  
  return float4(ambient * texColor + ((diffuse + addLight) * texColor + specular), gDiffMat.a);
}



technique PhongTextured
{
      pass P0
      {
            // Specify the vertex and pixel shader associated
            // with this pass.
            vertexShader = compile vs_1_1 PhongVS();
            pixelShader = compile ps_2_0 PhongPS();
      }
}

technique PhongNormalTextured
{
      pass P0
      {
            // Specify the vertex and pixel shader associated
            // with this pass.
            vertexShader = compile vs_1_1 PhongTBN_VS();
            pixelShader = compile ps_2_0 PhongNormalPS();
      }
}

technique PhongParallaxNormalTextured
{
      pass P0
      {
            // Specify the vertex and pixel shader associated
            // with this pass.
            vertexShader = compile vs_1_1 PhongTBN_VS();
            pixelShader = compile ps_2_0 PhongParallaxNormalPS();
      }
}

